// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package walk

import (
	"fmt"
	"go/constant"
	"internal/buildcfg"

	"cmd/compile/internal/base"
	"cmd/compile/internal/escape"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/staticinit"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// 重写树以使用单独的语句强制
// 评价顺序。让走路更容易，因为它
// 可以（在此运行之后）在表达式中随意重新排序。
// None
// 如果op为/或%，则将m[k]op=r重写为m[k]=m[k]op r。
// None
// 根据运行时例程的需要引入临时变量。
// 例如，map运行时例程采用map键
// 通过引用，因此请确保所有映射键都是可寻址的
// 根据需要将其复制给临时人员。
// 信道操作也是如此。
// None
// 安排贴图索引表达式仅以直接形式显示
// 赋值x=m[k]或m[k]=x，从不在较大的表达式中。
// None
// 安排接收表达式仅出现在直接赋值中
// x=<-c或作为独立语句<-c，不在较大的表达式中。

// TODO（rsc）：多次作业期间的临时介绍
// 应移到此文件中，以便可以清理临时文件
// 所以OAS2FUNC和OAS2RECV中隐含的转换
// 节点可以显式化，然后清除它们的临时对象。

// TODO（rsc）：转到和多级中断/继续可以跳过
// 插入了VARKILL注释。想办法解决这些问题。
// 当前实现是安全的，因为它将正确执行。
// 但它不会像可能的那样积极地重用临时表，而且
// 这可能会导致函数中这些变量不必要地归零
// 开场白

// orderState在订购过程中保存状态。
type orderState struct {
	out  []ir.Node             // 生成的语句列表
	temp []*ir.Name            // 临时变量堆栈
	free map[string][]*ir.Name // 未使用的临时表的自由列表，按类型。LongString（）。
	edit func(ir.Node) ir.Node // o.exprNoLHS的缓存关闭
}

// Order重写fn.Nbody以应用排序约束
// 在文件顶部的注释中描述。
func order(fn *ir.Func) {
	if base.Flag.W > 1 {
		s := fmt.Sprintf("\nbefore order %v", fn.Sym())
		ir.DumpList(s, fn.Body)
	}

	orderBlock(&fn.Body, map[string][]*ir.Name{})
}

// append TYPE检查stmt并将其附加到out。
func (o *orderState) append(stmt ir.Node) {
	o.out = append(o.out, typecheck.Stmt(stmt))
}

// newTemp分配具有给定类型的新临时文件，
// 将其推送到临时堆栈上并返回。
// 如果clear为true，newTemp将发出代码以将临时值归零。
func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
	var v *ir.Name
	// 注意：LongString接近我们想要的类型相等，
	// 但不完全如此。我们仍然需要再次检查类型。相同。
	key := t.LongString()
	a := o.free[key]
	for i, n := range a {
		if types.Identical(t, n.Type()) {
			v = a[i]
			a[i] = a[len(a)-1]
			a = a[:len(a)-1]
			o.free[key] = a
			break
		}
	}
	if v == nil {
		v = typecheck.Temp(t)
	}
	if clear {
		o.append(ir.NewAssignStmt(base.Pos, v, nil))
	}

	o.temp = append(o.temp, v)
	return v
}

// copyExpr的行为类似于newTemp，但也会发出
// 将临时值初始化为值n的代码。
func (o *orderState) copyExpr(n ir.Node) *ir.Name {
	return o.copyExpr1(n, false)
}

// copyExprClear类似于copyExpr，但在分配前清除临时值。
// 当tmp=n的评估变成
// 一种函数调用，传递一个指向临时对象的指针作为输出空间。
// 如果在写入tmp之前调用阻塞，
// 垃圾收集器仍会将临时垃圾视为活动垃圾，
// 所以我们必须在打电话之前把它调零。
// 今天，这只发生在通道接收操作中。
// （另一个候选者是地图访问，但不是地图访问
// 返回指向结果数据的指针，而不是使用指针
// （待填写。）
func (o *orderState) copyExprClear(n ir.Node) *ir.Name {
	return o.copyExpr1(n, true)
}

func (o *orderState) copyExpr1(n ir.Node, clear bool) *ir.Name {
	t := n.Type()
	v := o.newTemp(t, clear)
	o.append(ir.NewAssignStmt(base.Pos, v, n))
	return v
}

// cheapExpr返回n的廉价版本。
// 廉价的定义是n是一个变量或常数。
// 如果没有，cheapExpr分配一个新的tmp，发出tmp=n，
// 然后返回tmp。
func (o *orderState) cheapExpr(n ir.Node) ir.Node {
	if n == nil {
		return nil
	}

	switch n.Op() {
	case ir.ONAME, ir.OLITERAL, ir.ONIL:
		return n
	case ir.OLEN, ir.OCAP:
		n := n.(*ir.UnaryExpr)
		l := o.cheapExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.UnaryExpr)
		a.X = l
		return typecheck.Expr(a)
	}

	return o.copyExpr(n)
}

// safeExpr返回n的安全版本。
// safe的定义是n可以出现多次
// 在不违反原始程序语义的情况下，
// 分配给安全版本也有同样的效果
// 分配给原来的n。
// None
// 预期用途是在将x+=y重写为x=x+y时应用于x。
func (o *orderState) safeExpr(n ir.Node) ir.Node {
	switch n.Op() {
	case ir.ONAME, ir.OLITERAL, ir.ONIL:
		return n

	case ir.OLEN, ir.OCAP:
		n := n.(*ir.UnaryExpr)
		l := o.safeExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.UnaryExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.ODOT:
		n := n.(*ir.SelectorExpr)
		l := o.safeExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.SelectorExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.ODOTPTR:
		n := n.(*ir.SelectorExpr)
		l := o.cheapExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.SelectorExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.ODEREF:
		n := n.(*ir.StarExpr)
		l := o.cheapExpr(n.X)
		if l == n.X {
			return n
		}
		a := ir.SepCopy(n).(*ir.StarExpr)
		a.X = l
		return typecheck.Expr(a)

	case ir.OINDEX, ir.OINDEXMAP:
		n := n.(*ir.IndexExpr)
		var l ir.Node
		if n.X.Type().IsArray() {
			l = o.safeExpr(n.X)
		} else {
			l = o.cheapExpr(n.X)
		}
		r := o.cheapExpr(n.Index)
		if l == n.X && r == n.Index {
			return n
		}
		a := ir.SepCopy(n).(*ir.IndexExpr)
		a.X = l
		a.Index = r
		return typecheck.Expr(a)

	default:
		base.Fatalf("order.safeExpr %v", n.Op())
		return nil // 未达到
	}
}

// isaddrook报告是否可以将n的地址传递给运行时例程。
// 取一个变量的地址进行活性和优化分析
// 忘记变量的生命周期结束的位置。为了避免影响分析
// 在普通堆栈变量中，这些变量不是“isaddrook”。临时工没问题，
// 因为我们发出显式的VARKILL指令，标志着这些任务的结束
// 临时工的一生。
func isaddrokay(n ir.Node) bool {
	return ir.IsAddressable(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class == ir.PEXTERN || ir.IsAutoTmp(n))
}

// addrTemp确保n可以通过地址传递给运行时例程。
// 如果原始参数n不正确，addrTemp将创建一个tmp，并发出
// tmp=n，然后返回tmp。
// addrTemp的结果必须分配回n，例如。
// n、 左=o.addrTemp（n.Left）
func (o *orderState) addrTemp(n ir.Node) ir.Node {
	if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
		// TODO:是否将其扩展到所有静态复合文字节点？
		n = typecheck.DefaultLit(n, nil)
		types.CalcSize(n.Type())
		vstat := readonlystaticname(n.Type())
		var s staticinit.Schedule
		s.StaticAssign(vstat, 0, n, n.Type())
		if s.Out != nil {
			base.Fatalf("staticassign of const generated code: %+v", n)
		}
		vstat = typecheck.Expr(vstat).(*ir.Name)
		return vstat
	}
	if isaddrokay(n) {
		return n
	}
	return o.copyExpr(n)
}

// mapKeyTemp将n准备为映射运行时调用中的键，并返回n。
// 它应仅用于具有*\u fast*版本的映射运行时调用。
func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
	// 大多数映射调用都需要使用密钥的地址。
	// 例外情况：map*_fast*调用。见golang.org/issue/19015。
	alg := mapfast(t)
	if alg == mapslow {
		return o.addrTemp(n)
	}
	var kt *types.Type
	switch alg {
	case mapfast32:
		kt = types.Types[types.TUINT32]
	case mapfast64:
		kt = types.Types[types.TUINT64]
	case mapfast32ptr, mapfast64ptr:
		kt = types.Types[types.TUNSAFEPTR]
	case mapfaststr:
		kt = types.Types[types.TSTRING]
	}
	nt := n.Type()
	switch {
	case nt == kt:
		return n
	case nt.Kind() == kt.Kind(), nt.IsPtrShaped() && kt.IsPtrShaped():
		// 可以直接转换（例如，将命名类型转换为基础类型，或将一个指针转换为另一个指针）
		return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, kt, n))
	case nt.IsInteger() && kt.IsInteger():
		// 可直接转换（如int32到uint32）
		if n.Op() == ir.OLITERAL && nt.IsSigned() {
			// 避免常数溢出错误
			n = ir.NewConstExpr(constant.MakeUint64(uint64(ir.Int64Val(n))), n)
			n.SetType(kt)
			return n
		}
		return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONV, kt, n))
	default:
		// 不安全的内存转换。
		// 我们需要对kt型进行装载。创建一个kt类型的临时
		// 确保充分对齐。nt可能未对齐。
		if kt.Align < nt.Align {
			base.Fatalf("mapKeyTemp: key type is not sufficiently aligned, kt=%v nt=%v", kt, nt)
		}
		tmp := o.newTemp(kt, true)
		// *（*nt）（&tmp）=n
		var e ir.Node = typecheck.NodAddr(tmp)
		e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, nt.PtrTo(), e)
		e = ir.NewStarExpr(n.Pos(), e)
		o.append(ir.NewAssignStmt(base.Pos, e, n))
		return tmp
	}
}

// mapKeyReplaceStrConv将OBYTES2STR替换为OBYTES2STRTMP
// 在n中，以避免在地图查找中为键分配字符串。
// 返回一个bool，指示是否进行了修改。
// None
// 用于：
// x=m[字符串（k）]
// x=m[T1{…Tn{…，字符串（k），…}]
// 其中k是[]字节，T1到Tn是结构和数组文本的嵌套，
// 可以避免为字符串分配备份字节
// 通过重用[]字节备份数组。这些都是特例
// 用于在将字节片转换为字符串时避免分配。
// 一般来说，处理这些问题会很好，但因为
// []映射中不允许使用字节键，使用字符串（k）
// 在实践中的重要案例中出现。见第3512期。
func mapKeyReplaceStrConv(n ir.Node) bool {
	var replaced bool
	switch n.Op() {
	case ir.OBYTES2STR:
		n := n.(*ir.ConvExpr)
		n.SetOp(ir.OBYTES2STRTMP)
		replaced = true
	case ir.OSTRUCTLIT:
		n := n.(*ir.CompLitExpr)
		for _, elem := range n.List {
			elem := elem.(*ir.StructKeyExpr)
			if mapKeyReplaceStrConv(elem.Value) {
				replaced = true
			}
		}
	case ir.OARRAYLIT:
		n := n.(*ir.CompLitExpr)
		for _, elem := range n.List {
			if elem.Op() == ir.OKEY {
				elem = elem.(*ir.KeyExpr).Value
			}
			if mapKeyReplaceStrConv(elem) {
				replaced = true
			}
		}
	}
	return replaced
}

type ordermarker int

// markTemp返回临时变量堆栈的顶部。
func (o *orderState) markTemp() ordermarker {
	return ordermarker(len(o.temp))
}

// popTemp从堆栈中弹出临时值，直到达到标记，
// 必须由markTemp返回。
func (o *orderState) popTemp(mark ordermarker) {
	for _, n := range o.temp[mark:] {
		key := n.Type().LongString()
		o.free[key] = append(o.free[key], n)
	}
	o.temp = o.temp[:mark]
}

// cleantempnoop向*out发出VARKILL指令
// 对于临时堆栈上标记上方的每个临时堆栈。
// 它不会从堆栈中弹出临时对象。
func (o *orderState) cleanTempNoPop(mark ordermarker) []ir.Node {
	var out []ir.Node
	for i := len(o.temp) - 1; i >= int(mark); i-- {
		n := o.temp[i]
		out = append(out, typecheck.Stmt(ir.NewUnaryExpr(base.Pos, ir.OVARKILL, n)))
	}
	return out
}

// cleanTemp会为每个临时的
// 在临时堆栈上进行标记并将其从堆栈中删除。
func (o *orderState) cleanTemp(top ordermarker) {
	o.out = append(o.out, o.cleanTempNoPop(top)...)
	o.popTemp(top)
}

// stmtList对列表中的每个语句进行排序。
func (o *orderState) stmtList(l ir.Nodes) {
	s := l
	for i := range s {
		orderMakeSliceCopy(s[i:])
		o.stmt(s[i])
	}
}

// OrderMakeSicleCopy与模式匹配：
// m=奥马克斯利（[]T，x）；奥科比（南、南）
// 并将其改写为：
// m=OMAKESLICECOPY（[]T，x，s）；无
func orderMakeSliceCopy(s []ir.Node) {
	if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
		return
	}
	if len(s) < 2 || s[0] == nil || s[0].Op() != ir.OAS || s[1] == nil || s[1].Op() != ir.OCOPY {
		return
	}

	as := s[0].(*ir.AssignStmt)
	cp := s[1].(*ir.BinaryExpr)
	if as.Y == nil || as.Y.Op() != ir.OMAKESLICE || ir.IsBlank(as.X) ||
		as.X.Op() != ir.ONAME || cp.X.Op() != ir.ONAME || cp.Y.Op() != ir.ONAME ||
		as.X.Name() != cp.X.Name() || cp.X.Name() == cp.Y.Name() {
		// 这一行上面的行对于不同的相等运算符是正确的：
		// 我们希望as.X和cp.X是同一个名称，
		// 但我们希望初始数据来自不同的名称。
		return
	}

	mk := as.Y.(*ir.MakeExpr)
	if mk.Esc() == ir.EscNone || mk.Len == nil || mk.Cap != nil {
		return
	}
	mk.SetOp(ir.OMAKESLICECOPY)
	mk.Cap = cp.Y
	// 当m=OMAKESLICE（[]T，len（s））时设置有界；奥科比（南、南）
	mk.SetBounded(mk.Len.Op() == ir.OLEN && ir.SameSafeExpr(mk.Len.(*ir.UnaryExpr).X, cp.Y))
	as.Y = typecheck.Expr(mk)
	s[1] = nil // 删除单独的复制调用
}

// libfuzzer的边缘插入覆盖检测。
func (o *orderState) edge() {
	if base.Debug.Libfuzzer == 0 {
		return
	}

	// 创建要在节中分配的新uint8计数器
	// __libfuzzer\u额外计数器。
	counter := staticinit.StaticName(types.Types[types.TUINT8])
	counter.SetLibfuzzerExtraCounter(true)

	// 计数器+=1
	incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
	o.append(incr)
}

// orderBlock将n中的语句块排序为一个新的切片，
// 然后用新切片替换n中的旧切片。
// free是一个映射，可用于按类型获取临时变量。
func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
	var order orderState
	order.free = free
	mark := order.markTemp()
	order.edge()
	order.stmtList(*n)
	order.cleanTemp(mark)
	*n = order.out
}

// exprInPlace以*np和
// 将它们保留为最终*np的初始列表。
// exprInPlace的结果必须分配回n，例如。
// n、 左=o.exprInPlace（n.Left）
func (o *orderState) exprInPlace(n ir.Node) ir.Node {
	var order orderState
	order.free = o.free
	n = order.expr(n, nil)
	n = ir.InitExpr(order.out, n)

	// 按顺序插入新的临时表
	// 在外部列表的最前面。
	o.temp = append(o.temp, order.temp...)
	return n
}

// orderStmtInPlace命令单个语句*np的副作用
// 并将其替换为生成的语句列表。
// orderStmtInPlace的结果必须重新分配给n，例如。
// n、 左=订单stmtinplace（n.左）
// free是一个映射，可用于按类型获取临时变量。
func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
	var order orderState
	order.free = free
	mark := order.markTemp()
	order.stmt(n)
	order.cleanTemp(mark)
	return ir.NewBlockStmt(src.NoXPos, order.out)
}

// init将n的init列表移动到o.out。
func (o *orderState) init(n ir.Node) {
	if ir.MayBeShared(n) {
		// 为了并发安全，不要变异潜在的共享节点。
		// 首先，确保此处不需要任何工作。
		if len(n.Init()) > 0 {
			base.Fatalf("order.init shared node with ninit")
		}
		return
	}
	o.stmtList(ir.TakeInit(n))
}

// 调用命令调用表达式。
// n、 Op是OCALLMETH/OCALLFUNC/ocalliner或类似于OCOPY的内置对象。
func (o *orderState) call(nn ir.Node) {
	if len(nn.Init()) > 0 {
		// 调用方应该已经调用了o.init（nn）。
		base.Fatalf("%v with unexpected ninit", nn.Op())
	}

	// 内置函数。
	if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER {
		switch n := nn.(type) {
		default:
			base.Fatalf("unexpected call: %+v", n)
		case *ir.UnaryExpr:
			n.X = o.expr(n.X, nil)
		case *ir.ConvExpr:
			n.X = o.expr(n.X, nil)
		case *ir.BinaryExpr:
			n.X = o.expr(n.X, nil)
			n.Y = o.expr(n.Y, nil)
		case *ir.MakeExpr:
			n.Len = o.expr(n.Len, nil)
			n.Cap = o.expr(n.Cap, nil)
		case *ir.CallExpr:
			o.exprList(n.Args)
		}
		return
	}

	n := nn.(*ir.CallExpr)
	typecheck.FixVariadicCall(n)

	if isFuncPCIntrinsic(n) && isIfaceOfFunc(n.Args[0]) {
		// 对于内部/abi.FuncPCABIxxx（fn），如果fn是已定义的函数，
		// 不要在这里引入临时变量，这样更容易重写它
		// 在稍后的walk中标记地址引用。
		return
	}

	n.X = o.expr(n.X, nil)
	o.exprList(n.Args)

	if n.Op() == ir.OCALLINTER {
		return
	}
	keepAlive := func(arg ir.Node) {
		// 如果参数实际上是转换为uintptr的指针，
		// 安排指针保持活动状态，直到调用返回，
		// 通过将其复制到临时文件并标记该临时文件
		// 当我们弹出临时堆栈时，它仍然活着。
		if arg.Op() == ir.OCONVNOP {
			arg := arg.(*ir.ConvExpr)
			if arg.X.Type().IsUnsafePtr() {
				x := o.copyExpr(arg.X)
				arg.X = x
				x.SetAddrtaken(true) // 确保SSA保留x变量
				n.KeepAlive = append(n.KeepAlive, x)
			}
		}
	}

	// 检查逃生分析提供的“不安全uintptr”标签。
	for i, param := range n.X.Type().Params().FieldSlice() {
		if param.Note == escape.UnsafeUintptrNote || param.Note == escape.UintptrEscapesNote {
			if arg := n.Args[i]; arg.Op() == ir.OSLICELIT {
				arg := arg.(*ir.CompLitExpr)
				for _, elt := range arg.List {
					keepAlive(elt)
				}
			} else {
				keepAlive(arg)
			}
		}
	}
}

// mapAssign将n追加到o.out。
func (o *orderState) mapAssign(n ir.Node) {
	switch n.Op() {
	default:
		base.Fatalf("order.mapAssign %v", n.Op())

	case ir.OAS:
		n := n.(*ir.AssignStmt)
		if n.X.Op() == ir.OINDEXMAP {
			n.Y = o.safeMapRHS(n.Y)
		}
		o.out = append(o.out, n)
	case ir.OASOP:
		n := n.(*ir.AssignOpStmt)
		if n.X.Op() == ir.OINDEXMAP {
			n.Y = o.safeMapRHS(n.Y)
		}
		o.out = append(o.out, n)
	}
}

func (o *orderState) safeMapRHS(r ir.Node) ir.Node {
	// 确保在开始地图插入之前评估RHS。
	// 我们需要确保RHS不会惊慌失措。见第22881期。
	if r.Op() == ir.OAPPEND {
		r := r.(*ir.CallExpr)
		s := r.Args[1:]
		for i, n := range s {
			s[i] = o.cheapExpr(n)
		}
		return r
	}
	return o.cheapExpr(r)
}

// stmt命令语句n，并将其附加到o.out。
// 语句期间创建的临时表将被清除
// 尽可能使用VARKILL指令。
func (o *orderState) stmt(n ir.Node) {
	if n == nil {
		return
	}

	lno := ir.SetPos(n)
	o.init(n)

	switch n.Op() {
	default:
		base.Fatalf("order.stmt %v", n.Op())

	case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK:
		o.out = append(o.out, n)

	case ir.OAS:
		n := n.(*ir.AssignStmt)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, n.X)
		o.mapAssign(n)
		o.cleanTemp(t)

	case ir.OASOP:
		n := n.(*ir.AssignOpStmt)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, nil)

		if base.Flag.Cfg.Instrumenting || n.X.Op() == ir.OINDEXMAP && (n.AsOp == ir.ODIV || n.AsOp == ir.OMOD) {
			// 将m[k]op=r重写为m[k]=m[k]op-r-so
			// 我们可以确保如果op恐慌
			// 因为r是零，恐慌发生在
			// 地图分配。
			// DeepCopy在这里是个大锤子，但safeExpr
			// 确保没有太深的内容被复制。
			l1 := o.safeExpr(n.X)
			l2 := ir.DeepCopy(src.NoXPos, l1)
			if l2.Op() == ir.OINDEXMAP {
				l2 := l2.(*ir.IndexExpr)
				l2.Assigned = false
			}
			l2 = o.copyExpr(l2)
			r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil)
			as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r))
			o.mapAssign(as)
			o.cleanTemp(t)
			return
		}

		o.mapAssign(n)
		o.cleanTemp(t)

	case ir.OAS2:
		n := n.(*ir.AssignListStmt)
		t := o.markTemp()
		o.exprList(n.Lhs)
		o.exprList(n.Rhs)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 特别：避免复制函数调用n.对
	case ir.OAS2FUNC:
		n := n.(*ir.AssignListStmt)
		t := o.markTemp()
		o.exprList(n.Lhs)
		o.init(n.Rhs[0])
		o.call(n.Rhs[0])
		o.as2func(n)
		o.cleanTemp(t)

	// 特殊：使用临时变量保存结果，
	// 这样运行时就可以获取临时地址。
	// 无临时空白作业。
	// None
	// OAS2MAPR：如果需要，确保密钥可寻址，
	// 并确保OINDEXMAP未被复制。
	case ir.OAS2DOTTYPE, ir.OAS2RECV, ir.OAS2MAPR:
		n := n.(*ir.AssignListStmt)
		t := o.markTemp()
		o.exprList(n.Lhs)

		switch r := n.Rhs[0]; r.Op() {
		case ir.ODOTTYPE2:
			r := r.(*ir.TypeAssertExpr)
			r.X = o.expr(r.X, nil)
		case ir.ORECV:
			r := r.(*ir.UnaryExpr)
			r.X = o.expr(r.X, nil)
		case ir.OINDEXMAP:
			r := r.(*ir.IndexExpr)
			r.X = o.expr(r.X, nil)
			r.Index = o.expr(r.Index, nil)
			// 请参见下面OINDEXMAP的类似转换。
			_ = mapKeyReplaceStrConv(r.Index)
			r.Index = o.mapKeyTemp(r.X.Type(), r.Index)
		default:
			base.Fatalf("order.stmt: %v", r.Op())
		}

		o.as2ok(n)
		o.cleanTemp(t)

	// 特殊：不将n保存到out。
	case ir.OBLOCK:
		n := n.(*ir.BlockStmt)
		o.stmtList(n.List)

	// 特殊：n->left不是表达式；按原样保存。
	case ir.OBREAK,
		ir.OCONTINUE,
		ir.ODCL,
		ir.ODCLCONST,
		ir.ODCLTYPE,
		ir.OFALL,
		ir.OGOTO,
		ir.OLABEL,
		ir.OTAILCALL:
		o.out = append(o.out, n)

	// 特殊：处理调用参数。
	case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
		n := n.(*ir.CallExpr)
		t := o.markTemp()
		o.call(n)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.OCLOSE, ir.ORECV:
		n := n.(*ir.UnaryExpr)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.OCOPY:
		n := n.(*ir.BinaryExpr)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, nil)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
		n := n.(*ir.CallExpr)
		t := o.markTemp()
		o.exprList(n.Args)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 特殊：将参数排序为内部调用，而不是调用本身。
	case ir.ODEFER, ir.OGO:
		n := n.(*ir.GoDeferStmt)
		t := o.markTemp()
		o.init(n.Call)
		o.call(n.Call)
		if n.Call.Op() == ir.ORECOVER {
			// “延迟恢复（）”的特殊处理。我们需要评估FP
			// 包装前的争论。
			var init ir.Nodes
			n.Call = walkRecover(n.Call.(*ir.CallExpr), &init)
			o.stmtList(init)
		}
		if buildcfg.Experiment.RegabiDefer {
			o.wrapGoDefer(n)
		}
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.ODELETE:
		n := n.(*ir.CallExpr)
		t := o.markTemp()
		n.Args[0] = o.expr(n.Args[0], nil)
		n.Args[1] = o.expr(n.Args[1], nil)
		n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1])
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 从状态评估中清除临时值
	// 循环体的开头和for语句的后面。
	case ir.OFOR:
		n := n.(*ir.ForStmt)
		t := o.markTemp()
		n.Cond = o.exprInPlace(n.Cond)
		n.Body.Prepend(o.cleanTempNoPop(t)...)
		orderBlock(&n.Body, o.free)
		n.Post = orderStmtInPlace(n.Post, o.free)
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// 清理临时设备，使其处于最佳状态
	// 两个分支的开始。
	case ir.OIF:
		n := n.(*ir.IfStmt)
		t := o.markTemp()
		n.Cond = o.exprInPlace(n.Cond)
		n.Body.Prepend(o.cleanTempNoPop(t)...)
		n.Else.Prepend(o.cleanTempNoPop(t)...)
		o.popTemp(t)
		orderBlock(&n.Body, o.free)
		orderBlock(&n.Else, o.free)
		o.out = append(o.out, n)

	case ir.OPANIC:
		n := n.(*ir.UnaryExpr)
		t := o.markTemp()
		n.X = o.expr(n.X, nil)
		if !n.X.Type().IsEmptyInterface() {
			base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X)
		}
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.ORANGE:
		// n、 右图是正在排列的表达式。
		// 订购，如果需要的话，再复印一份。
		// 我们几乎总是这样做，以确保我们不会
		// 查看循环期间所做的任何值更改。
		// 通常，副本成本较低（例如，数组指针、，
		// 陈、片、线都很小）。
		// 例外情况是数组值的范围
		// （不是片，不是指向数组的指针），
		// 必须进行复制，以避免在复制过程中看到更新
		// 靶场主体。不过，在数组值上进行测距并不常见。

		// 标记[]字节（str）范围表达式以重用字符串备份存储。
		// 它是安全的，因为存储不能变异。
		n := n.(*ir.RangeStmt)
		if n.X.Op() == ir.OSTR2BYTES {
			n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
		}

		t := o.markTemp()
		n.X = o.expr(n.X, nil)

		orderBody := true
		xt := typecheck.RangeExprType(n.X.Type())
		switch xt.Kind() {
		default:
			base.Fatalf("order.stmt range %v", n.Type())

		case types.TARRAY, types.TSLICE:
			if n.Value == nil || ir.IsBlank(n.Value) {
				// 对于i:=范围x将只使用x一次来计算len（x）。
				// 不需要复制它。
				break
			}
			fallthrough

		case types.TCHAN, types.TSTRING:
			// chan、字符串、切片、数组范围多次使用值。
			// 复制。
			r := n.X

			if r.Type().IsString() && r.Type() != types.Types[types.TSTRING] {
				r = ir.NewConvExpr(base.Pos, ir.OCONV, nil, r)
				r.SetType(types.Types[types.TSTRING])
				r = typecheck.Expr(r)
			}

			n.X = o.copyExpr(r)

		case types.TMAP:
			if isMapClear(n) {
				// 保留贴图清除图案的主体，以便
				// 在行走过程中被检测到。将不使用循环体
				// 优化时，将范围循环转移到运行时调用。
				orderBody = false
				break
			}

			// 复制映射值（如果它是映射文字）。
			// TODO（rsc）：使tmp=文本表达式重用tmp。
			// 对于地图来说，tmp只是一个词，所以它几乎不重要。
			r := n.X
			n.X = o.copyExpr(r)

			// n、 Prealloc是迭代器的临时值。
			// MapIterType包含指针，需要归零。
			n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true)
		}
		n.Key = o.exprInPlace(n.Key)
		n.Value = o.exprInPlace(n.Value)
		if orderBody {
			orderBlock(&n.Body, o.free)
		}
		o.out = append(o.out, n)
		o.cleanTemp(t)

	case ir.ORETURN:
		n := n.(*ir.ReturnStmt)
		o.exprList(n.Results)
		o.out = append(o.out, n)

	// 特殊：清除每个区块条目中的临时案例。
	// Select必须输入其一个块，因此没有
	// 最后需要一次清洁。
	// 双重特殊：选择的评估顺序更严格
	// 而不是普通的表达。甚至像p.c
	// 必须将其吊入临时位置，以便
	// 在通道评估之后为不同的
	// 案例（如果p为零，则故障发生的时间将为
	// 把这个给我）。
	case ir.OSELECT:
		n := n.(*ir.SelectStmt)
		t := o.markTemp()
		for _, ncas := range n.Cases {
			r := ncas.Comm
			ir.SetPos(ncas)

			// 将任何新的正文序言附加到ninit。
			// 下一个循环将把ninit插入nbody。
			if len(ncas.Init()) != 0 {
				base.Fatalf("order select ninit")
			}
			if r == nil {
				continue
			}
			switch r.Op() {
			default:
				ir.Dump("select case", r)
				base.Fatalf("unknown op in select %v", r.Op())

			case ir.OSELRECV2:
				// 案例x，ok=<-c
				r := r.(*ir.AssignListStmt)
				recv := r.Rhs[0].(*ir.UnaryExpr)
				recv.X = o.expr(recv.X, nil)
				if !ir.IsAutoTmp(recv.X) {
					recv.X = o.copyExpr(recv.X)
				}
				init := ir.TakeInit(r)

				colas := r.Def
				do := func(i int, t *types.Type) {
					n := r.Lhs[i]
					if ir.IsBlank(n) {
						return
					}
					// 如果这是情况x:=<-ch或情况x，y:=<-ch，则情况为
					// 要声明x和y的ODCL节点。我们想推迟
					// 声明（和可能的分配），直到在箱体内部。
					// 在此处删除ODCL节点，并在下面的正文中重新创建它们。
					if colas {
						if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n {
							init = init[1:]
						}
						dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
						ncas.PtrInit().Append(dcl)
					}
					tmp := o.newTemp(t, t.HasPointers())
					as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, n, typecheck.Conv(tmp, n.Type())))
					ncas.PtrInit().Append(as)
					r.Lhs[i] = tmp
				}
				do(0, recv.X.Type().Elem())
				do(1, types.Types[types.TBOOL])
				if len(init) != 0 {
					ir.DumpList("ninit", r.Init())
					base.Fatalf("ninit on select recv")
				}
				orderBlock(ncas.PtrInit(), o.free)

			case ir.OSEND:
				r := r.(*ir.SendStmt)
				if len(r.Init()) != 0 {
					ir.DumpList("ninit", r.Init())
					base.Fatalf("ninit on select send")
				}

				// 案例c<-x
				// r->left是c，r->right是x，两者都会被计算。
				r.Chan = o.expr(r.Chan, nil)

				if !ir.IsAutoTmp(r.Chan) {
					r.Chan = o.copyExpr(r.Chan)
				}
				r.Value = o.expr(r.Value, nil)
				if !ir.IsAutoTmp(r.Value) {
					r.Value = o.copyExpr(r.Value)
				}
			}
		}
		// 既然我们已经积累了所有的临时材料，就把它们清理干净。
		// 还插入在上一个循环中排队的任何ninit。
		// （临时清洁必须在ninit工作之后进行。）
		for _, cas := range n.Cases {
			orderBlock(&cas.Body, o.free)
			cas.Body.Prepend(o.cleanTempNoPop(t)...)

			// 托多（mdempsky）：这真的有必要吗？
			// walkSelect显示为在Ninit中行走。
			cas.Body.Prepend(ir.TakeInit(cas)...)
		}

		o.out = append(o.out, n)
		o.popTemp(t)

	// 特殊：发送的值作为指针传递；使其可寻址。
	case ir.OSEND:
		n := n.(*ir.SendStmt)
		t := o.markTemp()
		n.Chan = o.expr(n.Chan, nil)
		n.Value = o.expr(n.Value, nil)
		if base.Flag.Cfg.Instrumenting {
			// 强制复制到堆栈，以便（chan T）（nil）<-x
			// 仍然作为x的读数进行检测。
			n.Value = o.copyExpr(n.Value)
		} else {
			n.Value = o.addrTemp(n.Value)
		}
		o.out = append(o.out, n)
		o.cleanTemp(t)

	// TODO（rsc）：更积极地清理临时空间。
	// 请注意，因为walkSwitch将重写
	// 切换到二进制搜索，这并不像看上去那么容易。
	// （如果我们在这里运行该代码，我们可以在
	// 而是使用if-else链。）
	// 现在，只需在最后清理所有临时文件。
	// 实际上，这很好。
	case ir.OSWITCH:
		n := n.(*ir.SwitchStmt)
		if base.Debug.Libfuzzer != 0 && !hasDefaultCase(n) {
			// 为检测添加空的“默认：”案例。
			n.Cases = append(n.Cases, ir.NewCaseStmt(base.Pos, nil, nil))
		}

		t := o.markTemp()
		n.Tag = o.expr(n.Tag, nil)
		for _, ncas := range n.Cases {
			o.exprListInPlace(ncas.List)
			orderBlock(&ncas.Body, o.free)
		}

		o.out = append(o.out, n)
		o.cleanTemp(t)
	}

	base.Pos = lno
}

func hasDefaultCase(n *ir.SwitchStmt) bool {
	for _, ncas := range n.Cases {
		if len(ncas.List) == 0 {
			return true
		}
	}
	return false
}

// exprList将表达式列表l排序为o。
func (o *orderState) exprList(l ir.Nodes) {
	s := l
	for i := range s {
		s[i] = o.expr(s[i], nil)
	}
}

// exprListInPlace命令表达式列表l，但保存
// 对单个表达式的副作用是ninit列表。
func (o *orderState) exprListInPlace(l ir.Nodes) {
	s := l
	for i := range s {
		s[i] = o.exprInPlace(s[i])
	}
}

func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
	return o.expr(n, nil)
}

// expr命令一个表达式，附加一个边
// 根据需要输出输出的效果。
// 如果这是赋值lhs=*np的一部分，则给出lhs。
// 否则lhs==nil。（当lhs！=无时，这可能是可能的
// 避免将表达式的结果复制到临时对象。）
// expr的结果必须分配回n，例如。
// n、 左=o.expr（n.左，左）
func (o *orderState) expr(n, lhs ir.Node) ir.Node {
	if n == nil {
		return n
	}
	lno := ir.SetPos(n)
	n = o.expr1(n, lhs)
	base.Pos = lno
	return n
}

func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
	o.init(n)

	switch n.Op() {
	default:
		if o.edit == nil {
			o.edit = o.exprNoLHS // 创建一次闭包
		}
		ir.EditChildren(n, o.edit)
		return n

	// 添加字符串将变成函数调用。
	// 分配一个临时文件来保存字符串。
	// 少于5个字符串使用直接运行时帮助程序。
	case ir.OADDSTR:
		n := n.(*ir.AddStringExpr)
		o.exprList(n.List)

		if len(n.List) > 5 {
			t := types.NewArray(types.Types[types.TSTRING], int64(len(n.List)))
			n.Prealloc = o.newTemp(t, false)
		}

		// 标记字符串（字节片）参数以重用字节片
		// 转换过程中的缓冲区。字符串串联不起作用
		// 记住字符串以便以后使用，这样比较安全。
		// 但是，只有当至少有一个非空字符串文本时，我们才能这样做。
		// 否则，如果所有其他参数都是空字符串，
		// concatstrings将返回对临时字符串的引用
		// 给打电话的人。
		hasbyte := false

		haslit := false
		for _, n1 := range n.List {
			hasbyte = hasbyte || n1.Op() == ir.OBYTES2STR
			haslit = haslit || n1.Op() == ir.OLITERAL && len(ir.StringVal(n1)) != 0
		}

		if haslit && hasbyte {
			for _, n2 := range n.List {
				if n2.Op() == ir.OBYTES2STR {
					n2 := n2.(*ir.ConvExpr)
					n2.SetOp(ir.OBYTES2STRTMP)
				}
			}
		}
		return n

	case ir.OINDEXMAP:
		n := n.(*ir.IndexExpr)
		n.X = o.expr(n.X, nil)
		n.Index = o.expr(n.Index, nil)
		needCopy := false

		if !n.Assigned {
			// 强制执行我们不复制的任何[]字节片
			// 无法在地图索引之前通过强制更改
			// 映射索引将立即发生在
			// 转换。请参阅下面几行的copyExpr。
			needCopy = mapKeyReplaceStrConv(n.Index)

			if base.Flag.Cfg.Instrumenting {
				// 种族检测器需要副本。
				needCopy = true
			}
		}

		// 密钥必须是可寻址的
		n.Index = o.mapKeyTemp(n.X.Type(), n.Index)
		if needCopy {
			return o.copyExpr(n)
		}
		return n

	// 具体类型（非接口）参数可能需要可寻址
	// 要传递到运行时转换例程的临时。
	case ir.OCONVIFACE:
		n := n.(*ir.ConvExpr)
		n.X = o.expr(n.X, nil)
		if n.X.Type().IsInterface() {
			return n
		}
		if _, _, needsaddr := convFuncName(n.X.Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
			// 如果我们需要将地址传递给转换函数，则需要一个临时值。
			// 我们还在这里处理静态复合文本节点，使一个命名的静态全局
			// 我们可以将其地址直接放在接口中（参见walk中的OConvirace案例）。
			n.X = o.addrTemp(n.X)
		}
		return n

	case ir.OCONVNOP:
		n := n.(*ir.ConvExpr)
		if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) {
			call := n.X.(*ir.CallExpr)
			// 将unsafe.Pointer（f（））重新排序到单独的
			// 语句，转换和函数调用必须保留
			// 在一起见golang.org/issue/15329。
			o.init(call)
			o.call(call)
			if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
				return o.copyExpr(n)
			}
		} else {
			n.X = o.expr(n.X, nil)
		}
		return n

	case ir.OANDAND, ir.OOROR:
		// ... = 左侧和右侧
		// None
		// 瓦尔布尔
		// r=LHS
		// 如果r{
		// r=RHS
		// }
		// ... = R

		n := n.(*ir.LogicalExpr)
		r := o.newTemp(n.Type(), false)

		// 评估左侧。
		lhs := o.expr(n.X, nil)
		o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, lhs)))

		// 右边求值，保存生成的代码。
		saveout := o.out
		o.out = nil
		t := o.markTemp()
		o.edge()
		rhs := o.expr(n.Y, nil)
		o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs)))
		o.cleanTemp(t)
		gen := o.out
		o.out = saveout

		// 如果左侧未导致短路，则向右侧发出。
		nif := ir.NewIfStmt(base.Pos, r, nil, nil)
		if n.Op() == ir.OANDAND {
			nif.Body = gen
		} else {
			nif.Else = gen
		}
		o.out = append(o.out, nif)
		return r

	case ir.OCALLFUNC,
		ir.OCALLINTER,
		ir.OCALLMETH,
		ir.OCAP,
		ir.OCOMPLEX,
		ir.OCOPY,
		ir.OIMAG,
		ir.OLEN,
		ir.OMAKECHAN,
		ir.OMAKEMAP,
		ir.OMAKESLICE,
		ir.OMAKESLICECOPY,
		ir.ONEW,
		ir.OREAL,
		ir.ORECOVER,
		ir.OSTR2BYTES,
		ir.OSTR2BYTESTMP,
		ir.OSTR2RUNES:

		if isRuneCount(n) {
			// len（[]符文）稍后将重写为runtime.countrunes。
			conv := n.(*ir.UnaryExpr).X.(*ir.ConvExpr)
			conv.X = o.expr(conv.X, nil)
		} else {
			o.call(n)
		}

		if lhs == nil || lhs.Op() != ir.ONAME || base.Flag.Cfg.Instrumenting {
			return o.copyExpr(n)
		}
		return n

	case ir.OAPPEND:
		// 检查附加（x，make（[]T，y）…）。
		n := n.(*ir.CallExpr)
		if isAppendOfMake(n) {
			n.Args[0] = o.expr(n.Args[0], nil) // 订单x
			mk := n.Args[1].(*ir.MakeExpr)
			mk.Len = o.expr(mk.Len, nil) // 订单y
		} else {
			o.exprList(n.Args)
		}

		if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.Args[0]) {
			return o.copyExpr(n)
		}
		return n

	case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
		n := n.(*ir.SliceExpr)
		n.X = o.expr(n.X, nil)
		n.Low = o.cheapExpr(o.expr(n.Low, nil))
		n.High = o.cheapExpr(o.expr(n.High, nil))
		n.Max = o.cheapExpr(o.expr(n.Max, nil))
		if lhs == nil || lhs.Op() != ir.ONAME && !ir.SameSafeExpr(lhs, n.X) {
			return o.copyExpr(n)
		}
		return n

	case ir.OCLOSURE:
		n := n.(*ir.ClosureExpr)
		if n.Transient() && len(n.Func.ClosureVars) > 0 {
			n.Prealloc = o.newTemp(typecheck.ClosureType(n), false)
		}
		return n

	case ir.OCALLPART:
		n := n.(*ir.SelectorExpr)
		n.X = o.expr(n.X, nil)
		if n.Transient() {
			t := typecheck.PartialCallType(n)
			n.Prealloc = o.newTemp(t, false)
		}
		return n

	case ir.OSLICELIT:
		n := n.(*ir.CompLitExpr)
		o.exprList(n.List)
		if n.Transient() {
			t := types.NewArray(n.Type().Elem(), n.Len)
			n.Prealloc = o.newTemp(t, false)
		}
		return n

	case ir.ODOTTYPE, ir.ODOTTYPE2:
		n := n.(*ir.TypeAssertExpr)
		n.X = o.expr(n.X, nil)
		if !types.IsDirectIface(n.Type()) || base.Flag.Cfg.Instrumenting {
			return o.copyExprClear(n)
		}
		return n

	case ir.ORECV:
		n := n.(*ir.UnaryExpr)
		n.X = o.expr(n.X, nil)
		return o.copyExprClear(n)

	case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
		n := n.(*ir.BinaryExpr)
		n.X = o.expr(n.X, nil)
		n.Y = o.expr(n.Y, nil)

		t := n.X.Type()
		switch {
		case t.IsString():
			// 标记字符串（字节片）参数以重用字节片
			// 转换过程中的缓冲区。字符串比较不起作用
			// 记住字符串以便以后使用，这样比较安全。
			if n.X.Op() == ir.OBYTES2STR {
				n.X.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
			}
			if n.Y.Op() == ir.OBYTES2STR {
				n.Y.(*ir.ConvExpr).SetOp(ir.OBYTES2STRTMP)
			}

		case t.IsStruct() || t.IsArray():
			// 对于复杂的比较，我们需要同时使用两个参数
			// 可寻址，以便我们可以将它们传递给运行时。
			n.X = o.addrTemp(n.X)
			n.Y = o.addrTemp(n.Y)
		}
		return n

	case ir.OMAPLIT:
		// 通过转换来订购地图：
		// 映射[int]int{
		// a（）：b（），
		// c（）：d（），
		// e（）：f（），
		// }
		// 到
		// m:=map[int]int{}
		// m[a（）]=b（）
		// m[c（）]=d（）
		// m[e（）]=f（）
		// 然后对结果进行排序。
		// 如果没有这种特殊情况，order将计算所有
		// 在将任何键和值存储到地图之前，先将它们删除。
		// 见第26552期。
		n := n.(*ir.CompLitExpr)
		entries := n.List
		statics := entries[:0]
		var dynamics []*ir.KeyExpr
		for _, r := range entries {
			r := r.(*ir.KeyExpr)

			if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
				dynamics = append(dynamics, r)
				continue
			}

			// 对一些静态条目进行递归排序可以将它们更改为动态条目；
			// e、 例如，oconvirface节点。见#31777。
			r = o.expr(r, nil).(*ir.KeyExpr)
			if !isStaticCompositeLiteral(r.Key) || !isStaticCompositeLiteral(r.Value) {
				dynamics = append(dynamics, r)
				continue
			}

			statics = append(statics, r)
		}
		n.List = statics

		if len(dynamics) == 0 {
			return n
		}

		// 发出贴图的创建（及其所有静态条目）。
		m := o.newTemp(n.Type(), false)
		as := ir.NewAssignStmt(base.Pos, m, n)
		typecheck.Stmt(as)
		o.stmt(as)

		// 发出eval+插入动态条目，一次一个。
		for _, r := range dynamics {
			as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value)
			typecheck.Stmt(as) // 注意：这会将OINDEX转换为OINDEXMAP
			o.stmt(as)
		}
		return m
	}

	// 上面没有返回类型断言。每个案例都必须回归自身。
}

// as2func命令OAS2FUNC节点。它创建临时表以确保从左到右的分配。
// 调用方应在调用order.as2func之前对分配的右侧进行排序。
// 它重写了，
// a、 b，a=。。。
// 像
// tmp1，tmp2，tmp3=。。。
// a、 b，a=tmp1，tmp2，tmp3
// 这是确保从左到右分配顺序所必需的。
func (o *orderState) as2func(n *ir.AssignListStmt) {
	results := n.Rhs[0].Type()
	as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)
	for i, nl := range n.Lhs {
		if !ir.IsBlank(nl) {
			typ := results.Field(i).Type
			tmp := o.newTemp(typ, typ.HasPointers())
			n.Lhs[i] = tmp
			as.Lhs = append(as.Lhs, nl)
			as.Rhs = append(as.Rhs, tmp)
		}
	}

	o.out = append(o.out, n)
	o.stmt(typecheck.Stmt(as))
}

// as2ok订购OAS2XXX并确认。
// 与as2func一样，这也添加了临时变量以确保从左到右的分配。
func (o *orderState) as2ok(n *ir.AssignListStmt) {
	as := ir.NewAssignListStmt(n.Pos(), ir.OAS2, nil, nil)

	do := func(i int, typ *types.Type) {
		if nl := n.Lhs[i]; !ir.IsBlank(nl) {
			var tmp ir.Node = o.newTemp(typ, typ.HasPointers())
			n.Lhs[i] = tmp
			as.Lhs = append(as.Lhs, nl)
			if i == 1 {
				// “ok”结果是根据Go的非类型化布尔值
				// 我们需要显式地将其转换为LHS类型
				// 后一种情况是定义的布尔类型（#8475）。
				tmp = typecheck.Conv(tmp, nl.Type())
			}
			as.Rhs = append(as.Rhs, tmp)
		}
	}

	do(0, n.Rhs[0].Type())
	do(1, types.Types[types.TBOOL])

	o.out = append(o.out, n)
	o.stmt(typecheck.Stmt(as))
}

var wrapGoDefer_prgen int

// wrapGoDefer将“go”或“defer”语句的目标包装为
// 新的“无参数函数”闭包。具体地说，它转换了
// None
// 延迟f（x，y）
// None
// 到
// None
// x1，y1:=x，y
// defer func（）{f（x1，y1）}（）
// None
// 这主要是为了能够根据
// 新寄存器ABI；通过进行此转换，我们可以简化
// 运行时中调用紧急路径上延迟的代码。
func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
	call := n.Call

	var callX ir.Node        // 被称为
	var callArgs []ir.Node   // 调用参数
	var keepAlive []*ir.Name // 保留通话列表（如果存在）

	// 用于在闭包内重新创建调用的助手。
	var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node

	// 延迟通话有多种形式和大小；不是所有的
	// 是呼叫者的。检查类型以查看我们正在处理的内容。
	switch x := call.(type) {
	case *ir.CallExpr:
		callX = x.X
		callArgs = x.Args
		keepAlive = x.KeepAlive
		mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
			newcall := ir.NewCallExpr(pos, op, fun, args)
			newcall.IsDDD = x.IsDDD
			return ir.Node(newcall)
		}
	case *ir.UnaryExpr: // 例：奥克洛斯
		callArgs = []ir.Node{x.X}
		mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
			if len(args) != 1 {
				panic("internal error, expecting single arg")
			}
			return ir.Node(ir.NewUnaryExpr(pos, op, args[0]))
		}
	case *ir.BinaryExpr: // ex:OCOPY
		callArgs = []ir.Node{x.X, x.Y}
		mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
			if len(args) != 2 {
				panic("internal error, expecting two args")
			}
			return ir.Node(ir.NewBinaryExpr(pos, op, args[0], args[1]))
		}
	default:
		panic("unhandled op")
	}

	// 如果调用的func没有参数、接收器和结果，则无需换行。
	// 但是在“defer func（）{…}（）”的情况下，我们需要
	// 防止directClosureCall重写的可能性
	// 使调用具有参数。
	// None
	// 不要包装方法调用（OCALLMETH，ocalliner），因为它有
	// 接受者。
	// None
	// 还要包装内置函数，因为它们可以扩展为
	// 带有参数的调用（例如OreColve）。
	// None
	// TODO:如果调用的函数没有参数和
	// 仅在注册结果中？
	if len(callArgs) == 0 && call.Op() == ir.OCALLFUNC && callX.Type().NumResults() == 0 {
		if c, ok := call.(*ir.CallExpr); ok && callX != nil && callX.Op() == ir.OCLOSURE {
			cloFunc := callX.(*ir.ClosureExpr).Func
			cloFunc.SetClosureCalled(false)
			c.PreserveClosure = true
		}
		return
	}

	if c, ok := call.(*ir.CallExpr); ok {
		// 为了简化事情，把f（a，b，[]T{c，d，e}…）转回去
		// 进入f（a，b，c，d，e）——当最后一个调用通过
		// 在下面输入checker，它将重建正确的切片文字。
		undoVariadic(c)
		callX = c.X
		callArgs = c.Args
	}

	// 如果我们生成的闭包逃逸，则此设置为true
	// （需要堆分配）。
	cloEscapes := func() bool {
		if n.Op() == ir.OGO {
			// 对于“go”，假设所有闭包都会逃逸。
			return true
		}
		// 对于延迟，只需使用任何逃避分析的结果即可
		// 已决定延期。
		return n.Esc() != ir.EscNever
	}()

	// 用于制作参数副本的助手。请注意，它是
	// 如果我们要放置一个
	// 参考闭合器中的温度（与
	// 复制它（按值），因为在按引用的情况下
	// 需要一个临时的，其生命周期延长到
	// 功能（相对于当前块或
	// 语句（正在排序）。
	mkArgCopy := func(arg ir.Node) *ir.Name {
		t := arg.Type()
		byval := t.Size() <= 128 || cloEscapes
		var argCopy *ir.Name
		if byval {
			argCopy = o.copyExpr(arg)
		} else {
			argCopy = typecheck.Temp(t)
			o.append(ir.NewAssignStmt(base.Pos, argCopy, arg))
		}
		// 下面的值128表示与代码一致
		// 在转义分析中，根据大小选择byval/byaddr。
		argCopy.SetByval(byval)
		return argCopy
	}

	// GetUnsaraFearg查找已删除的不安全的.Pointer arg
	// 以前捕获到呼叫的保留列表中，返回
	// 它的名称节点（如果找到）。
	getUnsafeArg := func(arg ir.Node) *ir.Name {
		// 查找uintptr（不安全的.指针（名称））
		if arg.Op() != ir.OCONVNOP {
			return nil
		}
		if !arg.Type().IsUintptr() {
			return nil
		}
		if !arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
			return nil
		}
		arg = arg.(*ir.ConvExpr).X
		argname, ok := arg.(*ir.Name)
		if !ok {
			return nil
		}
		for i := range keepAlive {
			if argname == keepAlive[i] {
				return argname
			}
		}
		return nil
	}

	// 将函数的参数复制到temp中。
	// None
	// 对于使用uintptr（不安全的.Pointer（…）参数的调用
	// 保持活动状态（请参阅（*orderState）中的代码。执行此操作的调用
	// 现有arg副本，而不是创建新副本。
	unsafeArgs := make([]*ir.Name, len(callArgs))
	origArgs := callArgs
	var newNames []*ir.Name
	for i := range callArgs {
		arg := callArgs[i]
		var argname *ir.Name
		unsafeArgName := getUnsafeArg(arg)
		if unsafeArgName != nil {
			// arg已被复制，请使用keepalive copy
			argname = unsafeArgName
			unsafeArgs[i] = unsafeArgName
		} else {
			argname = mkArgCopy(arg)
		}
		newNames = append(newNames, argname)
	}

	// 处理函数表达式（我们正在处理的
	// 调用）不是简单的函数符号。
	var fnExpr *ir.Name
	var methSelectorExpr *ir.SelectorExpr
	if callX != nil {
		switch {
		case callX.Op() == ir.ODOTMETH || callX.Op() == ir.ODOTINTER:
			// 处理方法调用的延迟，例如“延迟v.MyMethod（x，y）”
			n := callX.(*ir.SelectorExpr)
			n.X = mkArgCopy(n.X)
			methSelectorExpr = n
			if callX.Op() == ir.ODOTINTER {
				// 目前对于“defer i.M（）”，如果i为零，则在
				// defer语句的点，而不是在调用deferred函数时。
				// （我认为有一个问题需要讨论的是目的是什么
				// 但是我找不到。）
				// 我们需要在包装外进行零检查。
				tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
				c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, tab)
				c.SetTypecheck(1)
				o.append(c)
			}
		case !(callX.Op() == ir.ONAME && callX.(*ir.Name).Class == ir.PFUNC):
			// 处理“defer returnsafunc（）（x，y）”（用于
			// 示例）通过复制被调用方表达式。
			fnExpr = mkArgCopy(callX)
			if callX.Op() == ir.OCLOSURE {
				// 对于“延迟函数（…）”，除了复制
				// 关闭为临时，将其标记为不再直接关闭
				// 打电话。
				callX.(*ir.ClosureExpr).Func.SetClosureCalled(false)
			}
		}
	}

	// 创建一个新的无参数函数，我们将其交给Delay。
	var noFuncArgs []*ir.Field
	noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil)
	wrapGoDefer_prgen++
	outerfn := ir.CurFunc
	wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
	sym := types.LocalPkg.Lookup(wrapname)
	fn := typecheck.DeclFunc(sym, noargst)
	fn.SetIsHiddenClosure(true)
	fn.SetWrapper(true)

	// 用于捕获对外部作用域中声明的变量的引用的帮助器。
	capName := func(pos src.XPos, fn *ir.Func, n *ir.Name) *ir.Name {
		t := n.Type()
		cv := ir.CaptureName(pos, fn, n)
		cv.SetType(t)
		return typecheck.Expr(cv).(*ir.Name)
	}

	// 调用参数（x1，y1）需要作为新
	// 创建了闭包。
	newCallArgs := []ir.Node{}
	for i := range newNames {
		var arg ir.Node
		arg = capName(callArgs[i].Pos(), fn, newNames[i])
		if unsafeArgs[i] != nil {
			arg = ir.NewConvExpr(arg.Pos(), origArgs[i].Op(), origArgs[i].Type(), arg)
		}
		newCallArgs = append(newCallArgs, arg)
	}
	// 还可以将函数或方法表达式（如果需要）捕获到
	// 关闭。
	if fnExpr != nil {
		callX = capName(callX.Pos(), fn, fnExpr)
	}
	if methSelectorExpr != nil {
		methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name))
	}
	ir.FinishCaptureNames(n.Pos(), outerfn, fn)

	// 这将标记内置调用，而不是常规调用。
	irregular := (call.Op() != ir.OCALLFUNC &&
		call.Op() != ir.OCALLMETH &&
		call.Op() != ir.OCALLINTER)

	// 构造新的函数体：f（x1，y1）
	op := ir.OCALL
	if irregular {
		op = call.Op()
	}
	newcall := mkNewCall(call.Pos(), op, callX, newCallArgs)

	// 键入并检查结果。
	if !irregular {
		typecheck.Call(newcall.(*ir.CallExpr))
	} else {
		typecheck.Stmt(newcall)
	}

	// 完成正文，在主decls列表上注册函数。
	fn.Body = []ir.Node{newcall}
	typecheck.FinishFuncBody()
	typecheck.Func(fn)
	typecheck.Target.Decls = append(typecheck.Target.Decls, fn)

	// 创建闭包表达式
	clo := ir.NewClosureExpr(n.Pos(), fn)
	fn.OClosure = clo
	clo.SetType(fn.Type())

	// 设置闭包的转义属性。
	if n.Op() == ir.OGO {
		// 对于“go”，假设关闭将要退出
		// （运行时有一个例外，它没有
		// 允许堆分配的闭包）。
		if base.Ctxt.Pkgpath != "runtime" {
			clo.SetEsc(ir.EscHeap)
		}
	} else {
		// 对于延迟，只需使用任何逃避分析的结果即可
		// 已决定延期。
		if n.Esc() == ir.EscNever {
			clo.SetTransient(true)
			clo.SetEsc(ir.EscNone)
		}
	}

	// 通过argless函数创建新的顶级闭包调用。
	topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{})
	typecheck.Call(topcall)

	// 标记调用以确保directClosureCall不会撤消我们的工作。
	topcall.PreserveClosure = true

	fn.SetClosureCalled(false)

	// 最后，将defer语句指向新生成的调用。
	n.Call = topcall
}

// IsFuncPCInquired返回n是否是内部/abi.FuncPCABIxxx函数的直接调用。
func isFuncPCIntrinsic(n *ir.CallExpr) bool {
	if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME {
		return false
	}
	fn := n.X.(*ir.Name).Sym()
	return (fn.Name == "FuncPCABI0" || fn.Name == "FuncPCABIInternal") &&
		(fn.Pkg.Path == "internal/abi" || fn.Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "internal/abi")
}

// isIfaceOfFunc返回n是否是从func的直接引用进行的接口转换。
func isIfaceOfFunc(n ir.Node) bool {
	return n.Op() == ir.OCONVIFACE && n.(*ir.ConvExpr).X.Op() == ir.ONAME && n.(*ir.ConvExpr).X.(*ir.Name).Class == ir.PFUNC
}
